package com.tx.core.mybatis.service.impl;

import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;
import com.tx.core.exceptions.SILException;
import com.tx.core.exceptions.util.AssertUtils;
import com.tx.core.mybatis.dao.MybatisBaseDao;
import com.tx.core.mybatis.service.IBaseService;
import com.tx.core.paged.model.PagedList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * <pre>
 * 功能简述: MybatisBaseService
 * </pre>
 *
 * @author PengQingyang
 * 创建时间 [2024/4/2]
 */
public class MybatisBaseService<T, ID extends Serializable, M extends MybatisBaseDao<T, ID>> implements IBaseService<T, ID> {

    protected final Logger logger = LoggerFactory.getLogger(IBaseService.class);

    protected final Class<?>[] typeArguments = GenericTypeUtils.resolveTypeArguments(getClass(),
            MybatisBaseService.class);

    protected final Class<T> entityClass = currentModelClass();

    @Autowired
    protected M baseDao;

    /**
     * 获取对应的实体类型<br/>
     *
     * @return
     */
    @Override
    public Class<T> getEntityClass() {
        return this.entityClass;
    }

    /**
     * 返回持久层实现
     *
     * @return
     */
    public M getBaseDao() {
        return this.baseDao;
    }


    /**
     * 保存对象实例
     *
     * @param data 要保存的数据实体，类型为泛型 T
     */
    @Override
    public void save(T data) {
        AssertUtils.notNull(data, "entity is null.");

        this.baseDao.save(data);
    }

    /**
     * 获取实体类型
     *
     * @return
     */
    @Override
    public T newEntityInstance() {
        try {
            T entity = getEntityClass().getDeclaredConstructor().newInstance();
            return entity;
        } catch (InvocationTargetException | InstantiationException | IllegalAccessException |
                 NoSuchMethodException e) {
            throw new SILException("根据类型创建对象实例", e);
        }
    }

    /**
     * 保存对象实例
     *
     * @param data                   需要保存的数据实体。
     * @param updateWrapperGenerator 一个函数，接受一个数据实体，返回一个对该实体进行更新操作的包装器。这个包装器用于
     *                               包装更新操作的逻辑。
     */
    @Override
    public void save(T data, Function<T, Wrapper<T>> updateWrapperGenerator) {
        AssertUtils.notNull(data, "entity is null.");
        AssertUtils.notNull(updateWrapperGenerator, "updateWrapperGenerator is null.");

        this.baseDao.save(data, updateWrapperGenerator);
    }

    /**
     * 批量保存实体对象<br/>
     *
     * @param dataList         要保存的对象列表，不应为空。
     * @param batchSize        每次保存的批大小。通过调整此参数，可以控制数据库操作的频率，以优化性能。
     * @param useBatchExecutor 是否使用批量执行器。当设置为true时，将使用批量执行器来优化批量保存的性能。
     */
    @Override
    public void batchSave(List<T> dataList, int batchSize, boolean useBatchExecutor) {
        if (CollectionUtils.isEmpty(dataList)) {
            return;
        }
        this.baseDao.batchSave(dataList, batchSize, useBatchExecutor);
    }

    /**
     * 批量保存实体对象<br/>
     *
     * @param dataList               需要保存或更新的数据对象列表
     * @param updateWrapperGenerator 一个函数，用于为每个数据对象构建更新的条件包装器
     * @param batchSize              每次批量操作的大小
     * @param useBatchExecutor       是否使用批量执行器进行操作，这通常与数据库支持的批量操作相关
     */
    @Override
    public void batchSave(List<T> dataList, Function<T, Wrapper<T>> updateWrapperGenerator,
                          int batchSize, boolean useBatchExecutor) {
        if (CollectionUtils.isEmpty(dataList)) {
            return;
        }
        this.baseDao.batchSave(dataList, updateWrapperGenerator, batchSize, useBatchExecutor);
    }

    /**
     * 更新对象实例
     *
     * @param data 要更新的数据实体，类型为泛型 T
     */
    @Override
    public void insert(T data) {
        AssertUtils.notNull(data, "entity is null");

        this.baseDao.insert(data);
    }

    /**
     * 批量插入实体对象<br/>
     *
     * @param dataList         要插入的对象列表，不应为空。
     * @param batchSize        每次插入的批大小。通过调整此参数，可以控制数据库操作的频率，以优化性能。
     * @param useBatchExecutor 是否使用批量执行器。当设置为true时，将使用批量执行器来优化批量插入的性能。
     */
    @Override
    public void batchInsert(List<T> dataList, int batchSize, boolean useBatchExecutor) {
        if (CollectionUtils.isEmpty(dataList)) {
            return;
        }
        this.baseDao.batchSave(dataList, batchSize, useBatchExecutor);
    }

    /**
     * 删除对象实例
     *
     * @param pk 主键值，类型为泛型 ID
     */
    @Override
    public boolean deleteById(ID pk) {
        AssertUtils.notEmpty(pk, "pk is empty.");
        return this.baseDao.delete(pk);
    }

    /**
     * 删除对象实例
     *
     * @param entity 要删除的数据实体，类型为泛型 T
     */
    @Override
    public int delete(T entity) {
        AssertUtils.notNull(entity, "entity is null.");
        return this.baseDao.delete(entity);
    }

    /**
     * 批量删除实体对象<br/>
     *
     * @param dataList         要删除的对象列表，不应为空。
     * @param batchSize        每次删除的批大小。通过调整此参数，可以控制数据库操作的频率，以优化性能。
     * @param useBatchExecutor 是否使用批量执行器。当设置为true时，将使用批量执行器来优化批量删除的性能。
     */
    @Override
    public void batchDelete(List<T> dataList, int batchSize, boolean useBatchExecutor) {
        if (CollectionUtils.isEmpty(dataList)) {
            return;
        }
        this.baseDao.batchDelete(dataList, batchSize, useBatchExecutor);
    }

    /**
     * 更新对象实例
     *
     * @param id     主键值，类型为泛型 ID
     * @param entity 要更新的数据实体，类型为泛型 T
     */
    @Override
    public boolean updateById(ID id, T entity) {
        AssertUtils.notEmpty(id, "id is empty.");
        AssertUtils.notNull(entity, "entity is null.");

        return this.baseDao.update(id, entity);
    }

    /**
     * 更新对象实例
     *
     * @param entity 要更新的数据实体，类型为泛型 T
     */
    @Override
    public int update(T entity) {
        AssertUtils.notNull(entity, "entity is null.");

        return this.baseDao.update(entity);
    }

    /**
     * 更新对象
     *
     * @param id      主键，用于指定要更新的实体。
     * @param entity  要更新的实体对象。
     * @param wrapper 更新条件，用于指定更新哪些字段。
     * @return
     */
    @Override
    public boolean updateById(ID id, T entity, Wrapper<T> wrapper) {
        AssertUtils.notEmpty(id, "id is empty.");
        AssertUtils.notNull(entity, "entity is null.");
        AssertUtils.notNull(wrapper, "wrapper is null.");

        ((AbstractWrapper) wrapper).setEntity(entity);
        return this.baseDao.update(id, wrapper);
    }

    /**
     * 更新对象实例
     *
     * @param entity  要更新的数据实体，类型为泛型 T
     * @param wrapper 一个函数，用于为每个数据对象构建更新的条件包装器
     */
    @Override
    public int update(T entity, Wrapper<T> wrapper) {
        AssertUtils.notNull(entity, "entity is null.");
        AssertUtils.notNull(wrapper, "wrapper is null.");

        ((AbstractWrapper) wrapper).setEntity(entity);
        return this.baseDao.update(wrapper);
    }

    /**
     * 更新对象实例
     *
     * @param id                     主键值，类型为泛型 ID
     * @param entity                 要更新的数据实体，类型为泛型 T
     * @param updateWrapperGenerator 一个函数，用于为每个数据对象构建更新的条件包装器
     */
    @Override
    public boolean updateById(ID id, T entity, Function<T, Wrapper<T>> updateWrapperGenerator) {
        AssertUtils.notEmpty(id, "id is empty.");
        AssertUtils.notNull(entity, "entity is null.");
        AssertUtils.notNull(updateWrapperGenerator, "updateWrapperGenerator is null.");

        return this.baseDao.update(id, entity, updateWrapperGenerator);
    }

    /**
     * 更新对象实例
     *
     * @param entity                 要更新的数据实体，类型为泛型 T
     * @param updateWrapperGenerator 一个函数，用于为每个数据对象构建更新的条件包装器
     */
    @Override
    public int update(T entity, Function<T, Wrapper<T>> updateWrapperGenerator) {
        AssertUtils.notNull(entity, "entity is null.");
        AssertUtils.notNull(updateWrapperGenerator, "updateWrapperGenerator is null.");

        return this.baseDao.update(entity, updateWrapperGenerator);
    }

    /**
     * 批量更新实体对象<br/>
     *
     * @param entityList       需要更新的数据对象列表
     * @param batchSize        每次批量操作的大小
     * @param useBatchExecutor 是否使用批量执行器进行操作，这通常与数据库支持的批量操作相关
     */
    @Override
    public void batchUpdate(List<T> entityList, int batchSize, boolean useBatchExecutor) {
        if (CollectionUtils.isEmpty(entityList)) {
            return;
        }
        this.baseDao.batchUpdate(entityList, batchSize, useBatchExecutor);
    }

    /**
     * 批量更新实体对象<br/>
     *
     * @param entityList             需要更新的数据对象列表
     * @param updateWrapperGenerator 一个函数，用于为每个数据对象构建更新的条件包装器
     * @param batchSize              每次批量操作的大小
     * @param useBatchExecutor       是否使用批量执行器进行操作，这通常与数据库支持的批量操作相关
     */
    @Override
    public void batchUpdate(List<T> entityList, Function<T, Wrapper<T>> updateWrapperGenerator, int batchSize,
                            boolean useBatchExecutor) {
        if (CollectionUtils.isEmpty(entityList)) {
            return;
        }
        this.baseDao.batchUpdate(entityList, updateWrapperGenerator, batchSize, useBatchExecutor);
    }

    /**
     * 根据主键查询对象实例
     *
     * @param pk 主键值，类型为泛型 ID
     */
    @Override
    public T findById(ID pk) {
        AssertUtils.notEmpty(pk, "pk is empty.");
        return this.baseDao.find(pk);
    }

    /**
     * 根据实体对象查询对象实例
     *
     * @param entity 要查询的实体对象
     */
    @Override
    public T find(T entity) {
        AssertUtils.notNull(entity, "entity is null.");

        return this.baseDao.find(entity);
    }

    @Override
    public PagedList<T> queryPagedList(Map<String, Object> params, int pageIndex, int pageSize) {
        return this.baseDao.queryPagedList(params, pageIndex, pageSize);
    }

    @Override
    public PagedList<T> queryPagedList(Wrapper<T> wrapper, Map<String, Object> params, int pageIndex, int pageSize) {
        return this.baseDao.queryPagedList(wrapper, params, pageIndex, pageSize);
    }

    /**
     * 根据主键查询对象实例
     *
     * @param params    查询参数，以键值对形式提供
     * @param pageIndex 请求的页码，第几页，从1开始计数
     * @param pageSize  每页的记录数
     * @param count     总记录数，用于计算总页数
     * @return
     */
    @Override
    public PagedList<T> queryPagedList(Map<String, Object> params, int pageIndex, int pageSize, int count) {
        return this.baseDao.queryPagedList(params, pageIndex, pageSize, count);
    }

    @Override
    public PagedList<T> queryPagedList(Wrapper<T> wrapper, Map<String, Object> params, int pageIndex, int pageSize,
                                       int count) {
        return this.baseDao.queryPagedList(wrapper, params, pageIndex, pageSize, count);
    }

    @Override
    public List<T> queryList(Map<String, Object> params) {
        return this.baseDao.queryList(params);
    }

    @Override
    public List<T> queryList(Wrapper<T> wrapper, Map<String, Object> params) {
        return this.baseDao.queryList(wrapper, params);
    }

    @Override
    public int count(Map<String, Object> params, ID excludeId) {
        return this.baseDao.count(params, excludeId);
    }

    @Override
    public int count(Wrapper<T> wrapper, Map<String, Object> params, ID excludeId) {
        return this.baseDao.count(wrapper, params, excludeId);
    }

    protected Class<T> currentModelClass() {
        return (Class<T>) this.typeArguments[0];
    }

}
